/* 
 * En este archivo se implementan las funciones definidas
 * en el archivo autovalores.h
 */

#include "autovalores.h"
#include <stdlib.h>
#include <math.h>
#include <stdio.h>
#include <string.h>

/* Prototipado de funciones locales */
struct VV_PROPIO *crear_VV_PROPIO(int dimension);
int signo(double valor);


/* Implementación de funciones */

/*
 * Implementación de la función definida en cabecera.
 */
struct VV_PROPIO* metodoPotencias(struct MATRIZ *matriz) {

    // Declaración de variables
    int tamanio = matriz->dimension; // Tamaño de las matrices (n x n)
    long contadorIteraciones = 0;  // Contador de la cantidad de iteraciones que lleva el método
    double lambdaActual = 0;      // Buffer del autovalor obtenido en la iteracion actual
    double lambdaAnterior = 0;  // Buffer del autovalor obtenido en la iteracion anterior a la actual
    struct VECTOR *resultado = crearVector(tamanio); // Buffer del calculo de A*b

    // Elemento arbitrario seleccionado del autovector,
    // si es negativo es porque no se ha seleccionado todavia.
    int pivote = -1;

    // Creamos el objecto vv_propio y obtenemos su vector interno
    struct VV_PROPIO *vvPropio = crear_VV_PROPIO(tamanio);
    struct VECTOR *autoVector = vvPropio->autovector;

    // Inicializamos la autovector inicial como un vector de 1
    int i;
    for (i=0;i<tamanio;i++) {
        autoVector->elementos[i] = 1;
    }

    int condicionSalida; // Aquí guardaremos si se debe terminar de iterar o no.
    double errorActual; // Variación del autovalor con respecto a la iteración anterior

    do {

        // Buffereamos el lambda de la iteracion anterior
        lambdaAnterior = lambdaActual;

        // Multiplicamos la matriz A con el autovector actual
        multiplicar(matriz, autoVector, resultado);

        // Seleccionamos un elemento como pivot (si no se hizo previamente)
        if (pivote<0) {
            pivote = buscarMayorElemento(resultado);
        }

        // Seleccionamos el nuevo autovalor a partir del pivote seleccionado
        lambdaActual = resultado->elementos[pivote];

        // Calculamos el nuevo autovector
        for (i=0;i<tamanio;i++) {
            autoVector->elementos[i] = resultado->elementos[i] / lambdaActual;
        }

        // Calculamos el error actual
        errorActual = fabs((lambdaActual - lambdaAnterior) / lambdaActual);

        // Incrementamos la cantidad de iteraciones en 1
        contadorIteraciones++;

        /*
         * Iteramos hasta que se cumpla una de la siguientes condiciones:
         * 1) La diferencia porcentual entre el autovalor anterior y el actual
         *    sea menor que el error indicado por parámetro.
         * 2) Alcancemos el límite de iteraciones especificado por parámetro.
         */
        condicionSalida = errorActual <= MINIMO_ERROR
                || contadorIteraciones == MAX_CANT_ITERACIONES;

    } while (!condicionSalida);

    // Liberamos la memoria reservada
    eliminarVector(resultado);

    // Seteamos el autovalor calculado en el objeto VV_PROPIO
    vvPropio->autovalor = lambdaActual;

    // Devolvemos el autovector calculado y su autovalor asociado
    return vvPropio;

}


/*
 * Implementación de la función definida en cabecera.
 */
void eliminar_VV_PROPIO(struct VV_PROPIO *vvPropio) {

    // Eliminamos el vector
    eliminarVector(vvPropio->autovector);

    // Eliminamos el VV_PROPIO
    free(vvPropio);
    vvPropio = NULL;

}


/*
 * Implementación de la función definida en cabecera.
 */
double aproximarAutovalorMedio(double autovalor1, double autovalor2) {

    // Aqui guardaremos el valor calculado
    double autovalorAproximado;

    // Calculamos el autovalor de acuerdo al order de los autovalores
    // pasados por parámetro.
    if(signo(autovalor1) == signo(autovalor2)) {
        autovalorAproximado = (autovalor2 + autovalor1) / 2;
    } else {

        // Aproximamos el valor absoluto
        autovalorAproximado = (fabs(autovalor1) + fabs(autovalor2)) / 2;

        // Determinamos el autovalor dominante
        double autovalorDominante;
        
        if(fabs(autovalor1) > fabs(autovalor2)) {
            autovalorDominante = autovalor1;
        } else {
            autovalorDominante = autovalor2;
        }

        // Ajustamos el autovalor aproximado, según el signo del dominante.
        // Si el signo es negativo, también lo es el del aproximado.
        if(signo(autovalorDominante) == 0) {
            autovalorAproximado *= -1;
        }

    }

    // Devolver el autovalor aproximado
    return autovalorAproximado;

}

/*
 * Crea un objeto VV_PROPIO con un vector de dimensión especificada
 * y con autovalor 0.
 */
struct VV_PROPIO *crear_VV_PROPIO(int dimension) {

    // Creamos el objeto en sí
    struct VV_PROPIO *vvPropio;
    vvPropio = (struct VV_PROPIO *) malloc(sizeof(struct VV_PROPIO));

    // Le asignamos los valores iniciales
    vvPropio->autovector = crearVector(dimension);
    vvPropio->autovalor = 0;

    // retornamos el objeto creado
    return vvPropio;

}


/*
 * Devuelve 1 si el signo es positivo, 0 si el signo es negativo.
 */
int signo(double valor) {
    if (valor>=0) {
        return 1;
    } else {
        return 0;
    }
}